/*
 * Copyright 2002-2020 Intel Corporation.
 * 
 * This software is provided to you as Sample Source Code as defined in the accompanying
 * End User License Agreement for the Intel(R) Software Development Products ("Agreement")
 * section 1.L.
 * 
 * This software and the related documents are provided as is, with no express or implied
 * warranties, other than those that are expressly stated in the License.
 */

#include <iostream>
#include <fstream>
#include <vector>
#include "pin.H"
using std::cerr;
using std::ofstream;
using std::ios;
using std::string;
using std::endl;
using std::vector;

ofstream OutFile;

// Convenience data structure
typedef uint32_t reg_t;
struct Registers
{
	vector<reg_t> read;
	vector<reg_t> write;
};
struct Registers_BBL
{
	// read and write regs of all instructments of single bbl
	//vector<vector<reg_t>> read_BBL;
	//vector<vector<reg_t>> write_BBL;
	vector<Registers> regs_all;
};

// Global variables
// The array storing the distance frequency between two dependant instructions
UINT64 *insDependDistance;
INT32 maxSize;
INT32 insPointer = 0;
INT32 lastInsPointer[1024] = { 0 };

// This function is called before every instruction is executed. 
// You have to edit this function to determine the dependency distance
// and populate the insDependDistance data structure.
VOID updateInsDependDistance(VOID *v)
{
	// Update the instruction pointer
	

	// regs contains the registers read and written by this instruction.
	// regs->read contains the registers read.
	// regs->write contains the registers written.
	Registers_BBL *regs_bbl = (Registers_BBL*)v;
	//std::cout << "blk size:" << count << endl;
	//std::cout << "loop size:" << regs_bbl->regs_all.size() << endl;
	
	//for (vector<vector<reg_t>>::iterator regs = regs_bbl->regs_all.begin(); regs != regs_bbl->regs_all.end(); regs++)
	for(UINT32 i = 0; i < (regs_bbl->regs_all.size()); i++)
	{
		++insPointer;
		Registers regs = regs_bbl->regs_all[i];
		//printf("%ld\n",regs.write.size());
		//for(vector<reg_t>::iterator it = regs.write.begin(); it != regs.write.end(); it++)
		for(UINT32 j = 0; j < (regs.write.size()); j++)
		{
			reg_t it = regs.write[j];
			lastInsPointer[it] = insPointer;
			//printf("read reg is %d\n",it);
		}
		//for(vector<reg_t>::iterator it = regs.read.begin(); it != regs.read.end(); it++)
		for(UINT32 j = 0; j < (regs.read.size()); j++)
		{
			reg_t reg = regs.read[j];
			//printf("write reg is %d\n",reg);
			//reg_t reg = it;
			if(lastInsPointer[reg] > 0)
			{
				INT32 distance = insPointer - lastInsPointer[reg];
				//printf("%d\n",distance);
				if(distance <= maxSize)
				{
					insDependDistance[distance - 1] += 1;
				}
			}
		}
		
	}
}

VOID Trace(TRACE trace, VOID *v)
{
    // Visit every basic block  in the trace
    INS cur_ins;
    
    for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl))
    {
    	Registers_BBL* regs_bbl = new Registers_BBL();
    	// get the first instruction of bbl
    	for(cur_ins = BBL_InsHead(bbl); cur_ins != BBL_InsTail(bbl); cur_ins = INS_Next(cur_ins))
    	{
    		Registers* regs = new Registers();
   			// Find all the register written in singal instructment
			for (uint32_t iw = 0; iw < INS_MaxNumWRegs(cur_ins); iw++)
			{
				// 获取当前指令中被写的寄存器(即目的寄存器)
				REG wr = INS_RegW(cur_ins, iw);
				// 获取寄存器名
				wr = REG_FullRegName(wr);
				if (!REG_valid(wr))
				{
					continue;
    			}
    			// 将被写寄存器保存到regs向量当中
				if (std::find(regs->write.begin(), regs->write.end(), wr) == regs->write.end())
				{
					regs->write.push_back(wr);
				}

			}
			//regs_bbl->write_BBL.push_back(regs->write);
			for (uint32_t ir = 0; ir < INS_MaxNumWRegs(cur_ins); ir++)
			{
				REG rr = INS_RegR(cur_ins, ir);
				// 获取寄存器名
				rr = REG_FullRegName(rr);
				if (!REG_valid(rr))
				{
					continue;
    			}
				if (std::find(regs->read.begin(), regs->read.end(), rr) == regs->read.end())
				{
					regs->read.push_back(rr);
				}
			}
			//regs_bbl->read_BBL.push_back(regs->read);
    		regs_bbl->regs_all.push_back(*regs);
    	}
        // Insert a call to docount before every bbl, passing the number of instructions
        BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR)updateInsDependDistance, 
        	//IARG_UINT32, BBL_NumIns(bbl), 
        	IARG_PTR, (void*)regs_bbl, 
        	IARG_END);
    }
}
 

// This knob sets the output file name
KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool", "o", "insDependDistPro.csv", "specify the output file name");

// This knob will set the maximum distance between two dependant instructions in the program
KNOB<string> KnobMaxDistance(KNOB_MODE_WRITEONCE, "pintool", "s", "100", "specify the maximum distance between two dependant instructions in the program");

// This function is called when the application exits
VOID Fini(INT32 code, VOID *v)
{
	// Write to a file since cout and cerr maybe closed by the application
    OutFile.setf(ios::showbase);
    for (INT32 i = 0; i < maxSize; i++)
	    OutFile << insDependDistance[i] << ",";
    OutFile.close();
}

/* ===================================================================== */
/* Print Help Message                                                    */
/* ===================================================================== */

INT32 Usage()
{
    cerr << "This tool counts the number of dynamic instructions executed" << endl;
    cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
    return -1;
}

/* ===================================================================== */
/* Main                                                                  */
/* ===================================================================== */
/*   argc, argv are the entire command line: pin -t <toolname> -- ...    */
/* ===================================================================== */

int main(int argc, char * argv[])
{
    // Initialize pin
    if (PIN_Init(argc, argv)) return Usage();
    
    OutFile.open(KnobOutputFile.Value().c_str());
    maxSize = atoi(KnobMaxDistance.Value().c_str());

    // Initializing depdendancy Distance
    insDependDistance = new UINT64[maxSize];
    memset((void*)insDependDistance, 0, sizeof(UINT64) * maxSize);

    // Register Instruction to be called to instrument instructions
    //INS_AddInstrumentFunction(Instruction, 0);
	TRACE_AddInstrumentFunction(Trace, 0);
		
    // Register Fini to be called when the application exits
    PIN_AddFiniFunction(Fini, 0);
    
    // Start the program, never returns
    PIN_StartProgram();
    
    return 0;
}

